package io.geekidea.boot.system.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.read.listener.PageReadListener;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.geekidea.boot.auth.service.LoginRedisService;
import io.geekidea.boot.auth.util.LoginUtil;
import io.geekidea.boot.auth.util.TokenUtil;
import io.geekidea.boot.common.constant.CommonConstant;
import io.geekidea.boot.framework.exception.BusinessException;
import io.geekidea.boot.framework.page.OrderByItem;
import io.geekidea.boot.framework.page.OrderMapping;
import io.geekidea.boot.framework.page.Paging;
import io.geekidea.boot.system.dto.*;
import io.geekidea.boot.system.entity.SysUser;
import io.geekidea.boot.system.mapper.SysUserMapper;
import io.geekidea.boot.system.query.SysUserQuery;
import io.geekidea.boot.system.service.SysUserService;
import io.geekidea.boot.system.vo.SysUserVo;
import io.geekidea.boot.util.PagingUtil;
import io.geekidea.boot.util.PasswordUtil;
import io.geekidea.boot.util.UUIDUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 系统用户 服务实现类
 *
 * @author mr.wei
 * @since 2022-12-26
 */
@Slf4j
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private LoginRedisService LoginRedisService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean addSysUser(SysUserDto dto) {
        checkUsernameExists(dto.getUsername());
        SysUser sysUser = new SysUser();
        BeanUtils.copyProperties(dto, sysUser);
        // 密码加盐
        String salt = RandomStringUtils.randomNumeric(6);
        sysUser.setSalt(salt);
        String password = PasswordUtil.encrypt(sysUser.getPassword(), salt);
        sysUser.setPassword(password);
        // 保存用户
        return save(sysUser);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateSysUser(SysUserDto dto) {
        Long id = dto.getId();
        SysUser sysUser = getById(id);
        if (sysUser == null) {
            throw new BusinessException("系统用户不存在");
        }
        BeanUtils.copyProperties(dto, sysUser);
        return updateById(sysUser);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean deleteSysUser(Long id) {
        // 删除用户
        return removeById(id);
    }

    @Override
    public SysUserVo getSysUserById(Long id) {
        return sysUserMapper.getSysUserById(id);
    }

    @Override
    public Paging<SysUserVo> getSysUserPage(SysUserQuery query) {
        OrderMapping orderMapping = new OrderMapping();
        orderMapping.put("createTime", "create_time");
        PagingUtil.handlePage(query, orderMapping, OrderByItem.desc("id"));
        List<SysUserVo> list = sysUserMapper.getSysUserPage(query);
        Paging<SysUserVo> paging = new Paging<>(list);
        return paging;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean resetSysUserPassword(SysUserResetPasswordDto sysUserResetPasswordDto) {
        Long userId = sysUserResetPasswordDto.getUserId();
        log.info("管理员重置用户密码：" + userId);
        SysUser sysUser = getById(userId);
        if (sysUser == null) {
            throw new BusinessException("系统用户不存在");
        }
        String password = sysUserResetPasswordDto.getPassword();
        return handleUpdatePassword(userId, password);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateProfile(SysUserUpdateProfileDto sysUserUpdateProfileDto) {
        Long id = LoginUtil.getUserId();
        SysUser sysUser = getById(id);
        if (sysUser == null) {
            throw new BusinessException("用户信息不存在");
        }
        LambdaUpdateWrapper<SysUser> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.set(SysUser::getNickname, sysUserUpdateProfileDto.getNickname());
        lambdaUpdateWrapper.set(SysUser::getPhone, sysUserUpdateProfileDto.getPhone());
        lambdaUpdateWrapper.set(SysUser::getEmail, sysUserUpdateProfileDto.getEmail());
        lambdaUpdateWrapper.set(SysUser::getHead, sysUserUpdateProfileDto.getHead());
        lambdaUpdateWrapper.eq(SysUser::getId, id);
        boolean flag = update(lambdaUpdateWrapper);
        return flag;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updatePassword(SysUserUpdatePasswordDto sysUserUpdatePasswordDto) {
        Long id = LoginUtil.getUserId();
        SysUser sysUser = getById(id);
        if (sysUser == null) {
            throw new BusinessException("用户信息不存在");
        }
        // 验证旧密码
        String dbPassword = sysUser.getPassword();
        String dbSalt = sysUser.getSalt();
        String oldPassword = sysUserUpdatePasswordDto.getOldPassword();
        String encryptOldPassword = PasswordUtil.encrypt(oldPassword, dbSalt);
        if (!dbPassword.equals(encryptOldPassword)) {
            throw new BusinessException("旧密码错误");
        }
        // 验证两次密码是否一致
        String password = sysUserUpdatePasswordDto.getPassword();
        String confirmPassword = sysUserUpdatePasswordDto.getConfirmPassword();
        if (!password.equals(confirmPassword)) {
            throw new BusinessException("两次输入的密码不一致");
        }
        // 新密码不能与旧密码一致
        String newPassword = PasswordUtil.encrypt(password, dbSalt);
        if (dbPassword.equals(newPassword)) {
            throw new BusinessException("新密码不能与旧密码一致");
        }
        return handleUpdatePassword(id, password);
    }

    @Override
    public void checkUsernameExists(String username) {
        LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SysUser::getUsername, username);
        long count = count(wrapper);
        if (count > 0) {
            throw new BusinessException(username + "用户名已经存在");
        }
    }

    @Override
    public boolean importExcel(MultipartFile multipartFile) throws Exception {
        String originalFilename = multipartFile.getOriginalFilename();
        log.info("导入的Excel文件名称：" + originalFilename);
        String extension = FilenameUtils.getExtension(originalFilename).toLowerCase();
        if (!(extension.endsWith(CommonConstant.XLS) || extension.endsWith(CommonConstant.XLSX))) {
            throw new BusinessException("只能导入xls或xlsx格式的Excel文件");
        }
        InputStream inputStream = multipartFile.getInputStream();
//        文件数据少的时候直接跑
//        EasyExcel.read(inputStream, SysUserExcelDto.class, new PageReadListener<SysUserExcelDto>(dataList -> {
//            addSysUser(dataList);
//        }, 10)).sheet().doRead();

        //创建队列：用于存储从PageReadListener接收到的数据列
        ConcurrentLinkedQueue<List<SysUserExcelDto>> queue = new ConcurrentLinkedQueue<>();

        //当文件到数据多的时候 可以适当的增加线程
        int threadCount = 2;
        ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
        for (int i = 0; i < threadCount; i++) {
            executorService.submit(() -> {
                while (!Thread.currentThread().isInterrupted()) {
                    try {
                        List<SysUserExcelDto> dataList = queue.poll();
                        if (dataList != null) {
                            try {
                                addSysUser(dataList); // 将addSysUser放入try-catch块中
                            } catch (Exception ex) { // 捕获addSysUser抛出的异常
                                handleAddSysUserException(ex, dataList); // 自定义异常处理逻辑
                            }
                        } else {
                            Thread.sleep(100);
                        }
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt(); // 重置中断状态
                        throw new RuntimeException(e);
                    }
                }
            });
        }
        //将接收到的数据列表添加到队列中
        EasyExcel.read(inputStream, SysUserExcelDto.class, new PageReadListener<>(queue::offer, 10))
                .sheet()
                .doRead();

        executorService.shutdown();
        return true;
    }

    /**
     * 处理Excel导入用户数据入库
     * @param demoData
     */
    @Transactional(rollbackFor = Exception.class)
    public void addSysUser(List<SysUserExcelDto> demoData) {
        for (SysUserExcelDto dto : demoData) {
            log.info("读取到一条数据{}", JSON.toJSONString(demoData));

            //判断用户账号或手机号是否存在
            LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
            // 添加或条件
            queryWrapper.and(wrapper -> wrapper
                    .eq(SysUser::getUsername, dto.getUsername()).or().eq(SysUser::getPhone, dto.getPhone())
            );
            boolean exists = sysUserMapper.exists(queryWrapper);
            //数据存在时跳过当前
            if (exists){
                log.error("该数据账号或手机号已存在数据库：{}", JSON.toJSONString(dto));
                continue;
            }

            SysUser sysUser = new SysUser();
            BeanUtils.copyProperties(dto, sysUser);
            // 密码加盐
            String salt = RandomStringUtils.randomNumeric(6);
            sysUser.setSalt(salt);
            // todo 默认设备普通角色， 这里写死，实践业务跟一根据情况查询
            sysUser.setRoleId(497014598377477L);
            String password = PasswordUtil.encrypt(sysUser.getPassword(), salt);
            sysUser.setPassword(password);
            // 保存用户
            save(sysUser);
        }

    }

    /**
     * 处理addSysUser方法抛出的异常
     */
    private void handleAddSysUserException(Exception ex, List<SysUserExcelDto> dataList) {
        // 在这里实现具体的异常处理逻辑，例如记录日志、通知用户、回滚事务等
        System.err.println("Error adding SysUser data: " + ex.getMessage());
        ex.printStackTrace();
        // 可能还需要根据业务需求清理资源、更新状态或尝试重新添加数据等
    }



    /**
     * 修改密码并删除该用户当前的登录信息
     *
     * @param id
     * @param password
     * @return
     * @throws Exception
     */
    private boolean handleUpdatePassword(Long id, String password) {
        // 生产新的盐值
        String newSalt = UUIDUtil.getUuid();
        String newPassword = PasswordUtil.encrypt(password, newSalt);
        // 修改密码
        LambdaUpdateWrapper<SysUser> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.set(SysUser::getPassword, newPassword);
        lambdaUpdateWrapper.set(SysUser::getSalt, newSalt);
        lambdaUpdateWrapper.set(SysUser::getUpdateTime, new Date());
        lambdaUpdateWrapper.eq(SysUser::getId, id);
        // 清除当前用户登录信息
        LoginRedisService.deleteLoginInfoByToken(TokenUtil.getToken());
        return update(lambdaUpdateWrapper);
    }

}
